﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
using Chefbook.DataAccessLayer;
using Rhino.Mocks;

namespace UnitTests.DataObjectsTests
{
    [TestFixture]
    public class RecipeFixture
    {
        private MockRepository mocks;
        private Category root;
        private Recipe testRecipe;
        
        [SetUp]
        public void Init()
        {
            mocks = new MockRepository();
            root = new Category();
            testRecipe = new Recipe();
            root.AddRecipe(testRecipe);
            testRecipe.Id = 42;
        }

        [Test]
        public void ConstructorTest()
        {
            Assert.AreEqual("", testRecipe.Name, "Constructor Name test failed");
            Assert.AreEqual("", testRecipe.Description, "Constructor Description test failed");
            Assert.AreEqual("", testRecipe.Directions, "Constructor Directions test failed");
            Assert.AreEqual(TimeSpan.Zero, testRecipe.CookTime, "Constructor CookTime test failed");
            Assert.AreEqual(TimeSpan.Zero, testRecipe.PrepTime, "Constructor PrepTime test failed");
            Assert.AreEqual(1, testRecipe.Categories.Count((c)=> c == root), "Recipe's categories didn't containe exactly one copy of Root");
            Assert.AreEqual(1, root.DirectRecipes.Count((r) => r == testRecipe), "Root's recipes didn't contain exactly one copy of testRecipe");
            Assert.AreEqual(0, testRecipe.Ingredients.Count);
            Assert.AreEqual(42, testRecipe.Id);
        }

        [Test]
        public void RenameTest()
        {
            testRecipe.Name = "Beef Stew";
            Assert.AreEqual("Beef Stew", testRecipe.Name, "Rename test failed");
        }

        [Test]
        public void RenameNullTest()
        {
            Assert.Throws<System.Data.ConstraintException>(() => testRecipe.Name = null);
        }
        
        [Test]
        public void ChangeDescriptionTest()
        {
            testRecipe.Description = "Is a tasty beef stew";
            Assert.AreEqual("Is a tasty beef stew", testRecipe.Description, "Change Description test failed");
        }

        [Test]
        public void ChangeDescriptionNullTest()
        {
            Assert.Throws<System.Data.ConstraintException>(() => testRecipe.Description = null);
        }

        [Test]
        public void ChangeDirectionsTest()
        {
            testRecipe.Directions = "Is a tasty beef stew";
            Assert.AreEqual("Is a tasty beef stew", testRecipe.Directions, "Change Directions test failed");
        }

        [Test]
        public void ChangeDirectionsNullTest()
        {
            Assert.Throws<System.Data.ConstraintException>(() => testRecipe.Directions = null);
        }

        [Test]
        public void ChangeCookTimeTest()
        {
            testRecipe.CookTime = TimeSpan.FromMinutes(5);
            Assert.AreEqual(TimeSpan.FromMinutes(5), testRecipe.CookTime, "Change CookTime test failed");
        }
        
        [Test]
        public void ChangePrepTimeTest()
        {
            testRecipe.PrepTime = TimeSpan.FromHours(1.5);
            Assert.AreEqual(TimeSpan.FromHours(1.5), testRecipe.PrepTime, "Change PrepTime test failed");
        }

        [Test]
        public void DeleteTest()
        {
            Assert.Contains(testRecipe, root.DirectRecipes);
            testRecipe.Name = "Test";
            root.Name = "Root";
            testRecipe.Delete();
            var categories = testRecipe.Categories;
            foreach (var category in categories)
            {
                Assert.IsFalse(category.DirectRecipes.Contains(testRecipe), "Categories still contain recipe");
            }
        }

        [Test]
        public void ChangeCategoryTest1()
        {
            var cat1 = new Category();
            testRecipe.ChangeCategory(root, cat1);
            Assert.IsFalse(root.DirectRecipes.Contains(testRecipe), "Recipe not removed from old category");
            Assert.IsFalse(testRecipe.Categories.Contains(root), "Category not removed from recipe");
            Assert.Contains(cat1, testRecipe.Categories.ToList(), "Recipe does not contain new category");
            Assert.Contains(testRecipe, cat1.DirectRecipes, "New category does not contain recipe");
        }

        [Test]
        public void ChangeCategoryTest2()
        {
            var cat1 = new Category();
            var cat2 = new Category();
            Assert.Throws<ArgumentException>(() => testRecipe.ChangeCategory(cat2, cat1));
        }

        [Test]
        public void ChangeCategoryNullTest1()
        {
            var cat2 = new Category();
            Assert.Throws<ArgumentNullException>(() => testRecipe.ChangeCategory(cat2, null));
        }

        [Test]
        public void ChangeCategoryNullTest2()
        {
            var cat1 = new Category();
            Assert.Throws<ArgumentNullException>(() => testRecipe.ChangeCategory(null, cat1));
        }

        [Test]
        public void CreateIngredient()
        {
            var ingred = new Ingredient();
            testRecipe.AddIngredient(ingred);
            Assert.Contains(ingred, testRecipe.Ingredients.ToList());
            Assert.AreEqual(testRecipe, ingred.Recipe);
        }


        [Test]
        public void RemoveIngredient1()
        {
            var ingred = new Ingredient();
            testRecipe.AddIngredient(ingred);
            testRecipe.RemoveIngredient(ingred);
            Assert.False(testRecipe.Ingredients.Contains(ingred), "Ingredient wasn't removed from recipe");
        }

        [Test]
        public void RemoveIngredient2()
        {
            var ingred = new Ingredient();
            testRecipe.AddIngredient(ingred);
            testRecipe.RemoveIngredient(ingred);
            Assert.Throws<ArgumentException>(() => testRecipe.RemoveIngredient(ingred), "Removing ingredient that wasn't in list didn't throw ArgumentException");
        }

        [Test]
        public void RemoveIngredientNull()
        {
            Assert.Throws<ArgumentNullException>(() => testRecipe.RemoveIngredient(null));
        }
    }
}
